package com.ywd.platform.netty.jt080.service;

import com.ywd.platform.netty.jt080.po.*;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static ch.qos.logback.core.encoder.ByteArrayUtil.hexStringToByteArray;

/**
 * JT808协议解码器 (ByteBuf -> JT808Message)
 * 负责将ByteBuf解码为JT808Message对象
 * 消息 = 标识位 + 消息头 + 消息体 + 校验码
 * 帧解码后的消息：0x30 0x7E 0x08 0x7D 0x55
 * 输入：JT808FrameDecoder处理后的净荷数据（已去除头尾标识和校验码）
 * 输出：JT808Message对象（包含消息头和消息体）
 */
@Slf4j
//@ChannelHandler.Sharable
//@Component
public class JT808Decoder extends MessageToMessageDecoder<ByteBuf> {

    //协议默认字符集 (GBK)
    public final static String DEFAULT_CHARSET = "GBK";
    //终端手机号长度
    public final static int PHONE_NUMBER_LENGTH = 10;
    // 最小帧长度(12字节)
    public final static int MIN_FRAME_LENGTH = 12;

    /**
     * 消息解码
     *
     * @param ctx 上下文，获取 Channel、Pipeline 或触发事件（如异常传递）
     * @param in  输入数据缓冲区
     * @param out 解码的结果
     */
    @Override
    public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        log.info("[协议解码器] - 收到数据,可读字节数: {}", in.readableBytes());
        // 1.检查最小长度（消息头基本长度）
        if (in.readableBytes() < MIN_FRAME_LENGTH) {
            log.error("消息长度不足，等待更多数据: {}", in.readableBytes());
            //throw new CommonException("消息长度不足");
            return;
        }
        try {
            // 2.解析消息头
            JT808Header header = parseHeader(in);

            // 3.检查消息体长度是否足够
            int bodyLength = header.getMsgBodyLength();
            if (in.readableBytes() < bodyLength) {
                log.error("消息体长度不足,期望: {}, 实际: {}", bodyLength, in.readableBytes());
                //throw new CommonException("消息体长度不足");
                return;
            }

            // 4.解析消息体
            Object body = parseBody(header.getMsgId(), in, bodyLength);

            // 5.构建完整消息
            JT808Message message = new JT808Message();
            message.setHeader(header);
            message.setBody(body);

            //log.info("解析的消息头信息: {}", header);
            //log.info("解析的消息体信息: {}", body);


            //输出解析数据
            out.add(message);
            log.info("[协议解码器] - 解码成功 - 消息ID: 0x{}, 终端手机号: {}",
                    Integer.toHexString(header.getMsgId()), header.getTerminalPhone());
        } catch (Exception e) {
            log.error("[协议解码器] - 解码错误: {}", e.getMessage());
            //throw new DecoderException("消息解码错误", e);
        }
    }

    /**
     * 解析消息头
     */
    public JT808Header parseHeader(ByteBuf in) {
        JT808Header header = new JT808Header();
        // 检查是否有足够的数据解析消息头
        if (in.readableBytes() < 13) { // 最小头部长度
            log.error("可读数据不足，无法解析消息头");
            //throw new RuntimeException("数据不足，无法解析消息头");
            return null;
        }

        // 1. 消息ID (2字节)
        int msgId = in.readUnsignedShort();
        //log.info("消息ID: 0x{}", Integer.toHexString(msgId));
        header.setMsgId(msgId);

        // 2. 消息体属性 (2字节)
        int msgBodyProps = in.readUnsignedShort();
        //log.info("消息体属性: 0x{}", Integer.toHexString(msgBodyProps));
        header.setMsgBodyProps(msgBodyProps);

        // 3.消息体长度, 从消息体属性中提取消息体长度（低10位）
        int bodyLength = msgBodyProps & 0x3FF;
        //log.info("消息体长度: {} 字节", bodyLength);
        header.setMsgBodyLength(bodyLength);

        // 4. 协议版本号, 检查是否为2019版协议（消息体属性的第14位）
        boolean version = (msgBodyProps & 0x4000) == 0x4000;
        if (version) {
            if (in.readableBytes() < 1) {
                log.error("可读数据不足，无法读取协议版本号");
                //throw new RuntimeException("数据不足，无法读取协议版本号");
                return null;
            }
            int protocolVersion = in.readUnsignedByte();
            //log.info("协议版本号: {}", protocolVersion);
            header.setVersion(protocolVersion);
        } else {
            // 非2019版协议，设置默认版本号
            header.setVersion(0);
        }

        // 5. 终端手机号 (6字节BCD码)
        String phoneNumber = parsePhoneNumber(in);
        //log.info("手机号: {}", phoneNumber);
        header.setTerminalPhone(phoneNumber);

        // 6. 消息流水号 (2字节)
        int flowId = in.readUnsignedShort();
        //log.info("消息流水号: {}", flowId);
        header.setFlowId(flowId);

        // 7. 检查是否有分包（消息体属性的第13位）
        boolean hasSubPackage = (msgBodyProps & 0x2000) == 0x2000;
        //log.info("是否有分包: {}", hasSubPackage);
        if (hasSubPackage) {
            // 检查是否有足够的数据读取分包信息
            if (in.readableBytes() < 4) {
                log.error("可读数据不足，无法读取分包信息");
            }
            int totalPackage = in.readUnsignedShort();
            int packageNo = in.readUnsignedShort();
            //log.info("分包信息: 总包数={}, 包序号={}", totalPackage, packageNo);
            header.setTotalPackage(totalPackage);
            header.setPackageNo(packageNo);
        }

        return header;
    }

    /**
     * 解析终端手机号 (10字节BCD码，适用于JT808-2019协议)
     */
    public String parsePhoneNumber(ByteBuf in) {
        byte[] phoneBytes = new byte[PHONE_NUMBER_LENGTH];
        in.readBytes(phoneBytes);
        // 记录原始字节数据，便于调试
        // 将BCD码转换为字符串
        StringBuilder phoneBuilder = new StringBuilder();
        for (byte b : phoneBytes) {
            // 每个字节表示两个BCD数字
            int high = (b & 0xF0) >>> 4;  // 高4位
            int low = b & 0x0F;           // 低4位
            // 检查是否为有效BCD数字 (0-9)
            if (high <= 9) {
                phoneBuilder.append(high);
            } else {
                // 无效数字，使用原始十六进制值标记
                phoneBuilder.append(String.format("[%X]", high));
                log.error("检测到无效的BCD高位数字: {}", high);
            }
            if (low <= 9) {
                phoneBuilder.append(low);
            } else {
                // 无效数字，使用原始十六进制值标记
                phoneBuilder.append(String.format("[%X]", low));
                log.error("检测到无效的BCD低位数字: {}", low);
            }
        }
        String phoneNumber = phoneBuilder.toString();
        // 对于JT808-2019协议，手机号通常是20位数字（10字节BCD码）
        // 需要提取实际的11位手机号
        if (phoneNumber.length() == 20) {
            // 通常手机号位于后11位，前面是补零
            // 查找第一个非零数字的位置
            int firstNonZero = -1;
            for (int i = 0; i < phoneNumber.length(); i++) {
                if (phoneNumber.charAt(i) != '0' && phoneNumber.charAt(i) != '[') {
                    firstNonZero = i;
                    break;
                }
            }
            if (firstNonZero != -1) {
                // 提取从第一个非零数字开始的11位数字
                if (firstNonZero + 11 <= phoneNumber.length()) {
                    phoneNumber = phoneNumber.substring(firstNonZero, firstNonZero + 11);
                } else {
                    // 如果不足11位，提取所有剩余数字
                    phoneNumber = phoneNumber.substring(firstNonZero);
                    log.error("手机号长度不足11位: {}", phoneNumber);
                }
            } else {
                // 全部是零或无效字符
                phoneNumber = "0";
                log.error("手机号字段全为零或无效字符");
            }
        } else if (phoneNumber.length() > 20) {
            // 包含无效字符，尝试提取11位数字
            phoneNumber = extractPhoneNumber(phoneNumber);
        }
        //log.info("最终解析的手机号: {}", phoneNumber);
        return phoneNumber;
    }

    /**
     * 从可能包含无效字符的字符串中提取11位手机号
     */
    public String extractPhoneNumber(String rawNumber) {
        // 移除非数字字符（保留方括号内的十六进制值）
        StringBuilder cleaned = new StringBuilder();
        for (int i = 0; i < rawNumber.length(); i++) {
            char c = rawNumber.charAt(i);
            if (Character.isDigit(c)) {
                cleaned.append(c);
            } else if (c == '[') {
                // 处理无效字符标记
                int end = rawNumber.indexOf(']', i);
                if (end != -1) {
                    // 保留标记，但跳过处理
                    cleaned.append(rawNumber.substring(i, end + 1));
                    i = end;
                }
            }
        }
        String result = cleaned.toString();
        // 如果长度超过11位，尝试提取后11位
        if (result.length() > 11) {
            // 查找11位连续数字
            Pattern pattern = Pattern.compile("\\d{11}");
            Matcher matcher = pattern.matcher(result);
            if (matcher.find()) {
                result = matcher.group();
            } else {
                // 如果没有11位连续数字，取后11位（可能包含无效字符）
                result = result.substring(Math.max(0, result.length() - 11));
            }
        }
        return result;
    }

    /**
     * 根据消息ID解析消息体 (终端上行消息)
     */
    public Object parseBody(int msgId, ByteBuf in, int bodyLength) {
        return switch (msgId) {
            // 终端注册消息 0x0100
            case JT808Protocol.MSG_ID_TERMINAL_REGISTER -> {
                log.info("终端注册消息: 0x0100");
                yield parseRegisterBody(in);
            }
            // 终端鉴权消息 0x0102
            case JT808Protocol.MSG_ID_TERMINAL_AUTH -> {
                log.info("终端鉴权消息: 0x0102");
                yield parseAuthBody(in);
            }
            // 终端位置信息上报消息 0x0200
            case JT808Protocol.MSG_ID_LOCATION_REPORT -> {
                log.info("终端位置信息上报消息: 0x0200");
                yield parseLocationBody(in, bodyLength);
            }
            // 终端心跳消息 0x0002
            case JT808Protocol.MSG_ID_HEARTBEAT -> {
                log.info("终端心跳消息: 0x0002");
                yield parseHeartbeatBody(in);
            }
//                case JT808Protocol.MSG_ID_TERMINAL_PARAM_QUERY: //查询终端参数 0x0104
//                    log.info("查询终端参数");
//                    handleTerminalParamQuery(ctx, msg);
//                    break;
//                case JT808Protocol.MSG_ID_EVENT_REPORT:         //事件报告 0x0301
//                    log.info("事件报告");
//                    handleEventReport(ctx, msg);
//                    break;
//                case JT808Protocol.MSG_ID_DRIVER_INFO:          //驾驶员身份信息采集 0x0704
//                    log.info("驾驶员身份信息采集");
//                    handleDriverInfo(ctx, msg);
//                    break;
            default -> {
                log.warn("未知消息类型: 0x{}", Integer.toHexString(msgId));
                yield parseUnknownBody(in, bodyLength);
            }
        };
    }

    /**
     * 解析终端注册消息体(0x0100)
     */
    public RegisterBody parseRegisterBody(ByteBuf in) {
        RegisterBody body = new RegisterBody();

        try {
            // 记录原始读取位置，用于错误恢复
            in.markReaderIndex();
            // 省域ID (2字节)
            int provinceId = in.readUnsignedShort();
            //log.info("省域ID: {}", provinceId);
            body.setProvinceId(provinceId);
            // 市县域ID (2字节)
            int cityId = in.readUnsignedShort();
            //log.info("市县域ID: {}", cityId);
            body.setCityId(cityId);
            // 制造商ID (11字节)
            String manufacturerId = readString(in, 11);
            //log.info("制造商ID: {}", manufacturerId);
            body.setManufacturerId(manufacturerId);
            // 终端型号 (30字节)
            String terminalModel = readString(in, 30);
            //log.info("终端型号: {}", terminalModel);
            body.setTerminalModel(terminalModel);
            // 终端ID (30字节)
            String terminalId = readString(in, 30);
            //log.info("终端ID: {}", terminalId);
            body.setTerminalId(terminalId);
            // 车牌颜色 (1字节)
            byte plateColor = in.readByte();
            //log.info("车牌颜色: {}", plateColor);
            body.setPlateColor(plateColor);
            // 车辆车牌号 (变长)
            // 注意：根据JT808协议，车牌号是变长字段，但通常有最大长度限制
            int licensePlateLength = in.readableBytes();
            String licensePlate = readString(in, licensePlateLength);
            //log.info("车辆车牌号: {}", licensePlate);
            body.setLicensePlate(licensePlate);

            return body;
        } catch (Exception e) {
            // 发生异常时重置读取位置
            in.resetReaderIndex();
            log.error("解析终端注册消息体失败: {}", e.getMessage(), e);
            //throw new RuntimeException("解析终端注册消息体失败", e);
            return null;
        }
    }

    /**
     * 读取指定长度的字符串
     */
    public String readString(ByteBuf in, int length) {
        if (length <= 0 || in.readableBytes() < length) {
            log.error("无效的读取长度或数据不足: 期望长度={}, 实际可读={}", length, in.readableBytes());
            return "";
        }
        byte[] bytes = new byte[length];
        in.readBytes(bytes);
        // 去除字符串中的空字符和不可见字符
        String result = new String(bytes).trim();
        // 如果全是空字符，返回空字符串
        if (result.isEmpty() || result.replace("\0", "").isEmpty()) {
            return "";
        }
        return result;
    }

    /**
     * 解析终端鉴权消息体(0x0102)
     */
    public AuthBody parseAuthBody(ByteBuf in) {
        AuthBody body = new AuthBody();
        // 鉴权码长度 (1字节)
        int authCodeLength = in.readUnsignedByte();
        //log.info("鉴权码长度, {}", authCodeLength);
        body.setAuthCodeLength(authCodeLength);
        // 鉴权码
        String authCode = readString(in, authCodeLength);
        //log.info("鉴权码, {}", authCode);
        body.setAuthCode(authCode);
        // IMEI
        String imei = readString(in, 15);
        //log.info("IMEI, {}", imei);
        body.setImei(imei);
        // 软件版本号
        String softwareVersion = readString(in, 20);
        //log.info("软件版本号, {}", softwareVersion);
        body.setSoftwareVersion(softwareVersion);

        return body;
    }

    /**
     * 解析位置上报消息体(0x0200)
     *
     */
    public LocationBody parseLocationBody(ByteBuf in, int bodyLength) {
        LocationBody body = new LocationBody();
        // 记录原始位置，用于异常处理
        int startReaderIndex = in.readerIndex();

        // 1. 报警标志 (4字节) - 协议中是4字节
        long alarmFlag = in.readUnsignedInt();
        body.setAlarmFlag(alarmFlag);
        //log.info("报警标志: 0x{} (十进制:{})", Long.toHexString(alarmFlag), alarmFlag);

        // 2. 状态标志 (4字节) - 协议中是4字节
        long statusFlag = in.readUnsignedInt();
        body.setStatusFlag(statusFlag);
        //log.info("状态标志: 0x{} (十进制:{})", Long.toHexString(statusFlag), statusFlag);

        // 3. 纬度 (4字节) - 转换为度
        double latitude = in.readUnsignedInt() / 1000000.0;
        body.setLatitude(latitude);
        //log.info("纬度: {}(度)", latitude);

        // 4. 经度 (4字节) - 转换为度
        double longitude = in.readUnsignedInt() / 1000000.0;
        body.setLongitude(longitude);
        //log.info("经度: {}(度)", longitude);

        // 5. 高程 (2字节)
        int altitude = in.readUnsignedShort();
        body.setAltitude(altitude);
        //log.info("高程: {}(米)", altitude);

        // 6. 速度 (2字节) - 转换为km/h
        double speed = in.readUnsignedShort() / 10.0;
        body.setSpeed(speed);
        //log.info("速度: {}(km/h)", speed);

        // 7. 方向 (2字节) - 0-359度
        int direction = in.readUnsignedShort();
        body.setDirection(direction);
        //log.info("方向: {}(度)", direction);

        // 8. 时间 (6字节BCD码)
        String gpsTime = parseBcdTime(in);
        body.setGpsTime(gpsTime);
        //log.info("定位时间: {}", gpsTime);

        // 9. 附加信息（剩余字节）
        int bytesLeft = bodyLength - (in.readerIndex() - startReaderIndex);
        if (bytesLeft > 0) {
            //log.info("解析附加信息: {}字节", bytesLeft);
            Map<Integer, byte[]> extraInfo = parseExtraInfo(in, bytesLeft);
            body.setExtraInfo(extraInfo);
        }

        // 10. 解析报警标志详细
        parseAlarmDetails(body, alarmFlag);

        // 11. 解析状态标志详细
        parseStatusDetails(body, statusFlag);

        //log.info("位置信息: {}", body);
        return body;
    }


    /**
     * 解析BCD时间格式(yyMMddHHmmss → yyyy-MM-dd HH:mm:ss)
     */
    private String parseBcdTime(ByteBuf in) {
        byte[] timeBytes = new byte[6];
        in.readBytes(timeBytes);
        // 验证BCD时间有效性
        for (byte b : timeBytes) {
            if ((b & 0xF0) > 0x90 || (b & 0x0F) > 0x09) {
                log.error("无效的BCD时间字节");
            }
        }
        try {
            // 直接转换为LocalDateTime
            int year = (timeBytes[0] >> 4 & 0x0F) * 10 + (timeBytes[0] & 0x0F);
            int month = (timeBytes[1] >> 4 & 0x0F) * 10 + (timeBytes[1] & 0x0F);
            int day = (timeBytes[2] >> 4 & 0x0F) * 10 + (timeBytes[2] & 0x0F);
            int hour = (timeBytes[3] >> 4 & 0x0F) * 10 + (timeBytes[3] & 0x0F);
            int minute = (timeBytes[4] >> 4 & 0x0F) * 10 + (timeBytes[4] & 0x0F);
            int second = (timeBytes[5] >> 4 & 0x0F) * 10 + (timeBytes[5] & 0x0F);
            // 添加2000年修正
            int fullYear = 2000 + year;
            // 创建日期时间
            LocalDateTime dateTime = LocalDateTime.of(
                    fullYear, month, day, hour, minute, second
            );
            // 格式化为字符串
            return dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
        } catch (Exception e) {
            log.error("无效的时间值: 原始={}", ByteBufUtil.hexDump(timeBytes));
            // 回退到原始显示方法
            return String.format("%02d%02d%02d%02d%02d%02d",
                    timeBytes[0], timeBytes[1], timeBytes[2],
                    timeBytes[3], timeBytes[4], timeBytes[5]);
        }
    }

    /**
     * 解析附加信息
     */
    private Map<Integer, byte[]> parseExtraInfo(ByteBuf in, int totalBytes) {
        Map<Integer, byte[]> extraInfo = new HashMap<>();
        int bytesParsed = 0;
        while (bytesParsed < totalBytes) {
            // 附加信息ID (1字节)
            int id = in.readUnsignedByte();
            bytesParsed++;
            // 获取长度，确保不超过剩余字节
            if (in.readableBytes() < 1) {
                log.error("附加信息长度字节缺失: ID=0x{}", Integer.toHexString(id));
                break;
            }
            int length = in.readUnsignedByte();
            bytesParsed++;
            // 验证数据长度
            if (in.readableBytes() < length) {
                log.error("附加信息数据不完整: ID=0x{}, 需要{}字节, 实际{}字节",
                        Integer.toHexString(id), length, in.readableBytes());
                break;
            }
            // 读取数据
            byte[] value = new byte[length];
            in.readBytes(value);
            bytesParsed += length;
            extraInfo.put(id, value);
            //log.info("附加信息: ID=0x{}, 长度={}, 内容={}", Integer.toHexString(id), length, ByteBufUtil.hexDump(value));
        }
        return extraInfo;
    }

    /**
     * 报警标志详细解析
     */
    private void parseAlarmDetails(LocationBody body, long alarmFlag) {
        Map<AlarmType, Boolean> map = new EnumMap<>(AlarmType.class);
        // 遍历所有可能的报警位
        for (AlarmType alarm : AlarmType.values()) {
            boolean active = (alarmFlag & alarm.getBitMask()) != 0;
            map.put(alarm, active);
        }
        body.setAlarmMap(map);
    }

    /**
     * 状态标志详细解析
     */
    private void parseStatusDetails(LocationBody body, long statusFlag) {
        Map<StatusFlag, Boolean> statusDetails = new EnumMap<>(StatusFlag.class);
        // 解析各个状态位
        for (StatusFlag status : StatusFlag.values()) {
            boolean active = (statusFlag & status.getBitMask()) != 0;
            statusDetails.put(status, active);
        }
        body.setStatusMap(statusDetails);
    }

    /**
     * 解析心跳消息体(0x0002)
     */
    public Object parseHeartbeatBody(ByteBuf in) {
        // 心跳消息没有消息体，但需要验证消息体长度是否为0
        if (in.readableBytes() > 0) {
            log.error("心跳消息包含意外的消息体数据，长度: {}", in.readableBytes());
            // 跳过额外数据
            in.skipBytes(in.readableBytes());
        }
        return new HeartbeatBody();
    }

    /**
     * 解析未知消息体
     */
    public UnknownBody parseUnknownBody(ByteBuf in, int bodyLength) {
        UnknownBody body = new UnknownBody();
        byte[] data = new byte[bodyLength];
        in.readBytes(data);
        body.setRawData(data);
        return body;
    }

}